#include <stdio.h>
#include <math.h>
#include <tiffio.h>

signed short readImage( char *, char ** );

int main(int argc, char *argv[])
{
	TIFF *image;
	uint16 photo, bps, spp, fillorder;
	uint32 width, height, row, col, lookup;
	int32 idx;
	float frc,x;
	tsize_t stripSize;
	unsigned long imageOffset, result;
	int stripMax, stripCount;
	unsigned char *buffer, tempbyte;
	unsigned char *transformedImg; 
	unsigned char *mate; 
	unsigned long bufferSize, count;
	unsigned char intensity, intensity0, intensity1;

	char * ptt_buff;

	if( readImage( "string", &ptt_buff ) < 0 ){
		printf("Cannot allocate image memory\n");
		exit(42);
	}

	if( argc < 2 ) {
		printf("ptc tool - 'Perspective Transformation C' program.\n");
		printf("Usage:    ptc <filename>\n\n");
		printf("   filename  Full qualified path to the TIFF file containing\n");
		printf("             a specific grayscale image for this demonstration.\n");
		printf("\nDescription:\n");
		printf("   This small application demonstrates perspective transformation caclculation in C\n");
		printf("   The input for the transformation is not calculated in the program, it comes\n");
		printf("   from matlab code. The calculation is actually trivial, it is only tedious to \n");
		printf("   recode the matrix inversion and multiplication i C.\n\n");
		printf("   The application is using the libTIFF library to open one specific TIFF file. \n");
		printf("   This file cannot be an arbitarry file. The cooeficients for perspective \n");
		printf("   transformation must match the image distortion, of course. Further, the \n");
		printf("   transfor ation is using a couple of image specific constants and properties. \n");
		printf("   It will not be the same for another image.\n\n");
		printf("   The example uses a triangel as a convolution kernel, which results in linear\n");
		printf("   regression for conculating missing positions in the image signal.\n\n");
		printf("   The tool is freeware. Martin Metal (c) 2014.\n");
		exit(1);
	}
	
	/* READ ORIGINIAL IMAGE */
	// <<<
	//
	// Open the TIFF image
	if((image = TIFFOpen(argv[1], "r")) == NULL){
		fprintf(stderr, "Could not open incoming image\n");
		exit(42);
	}

	// Check that it is of a type that we support
	if((TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bps) == 0) || (bps != 8)){
		fprintf(stderr, "Either undefined or unsupported number of bits per sample\n");
		exit(42);
	}

	if((TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp) == 0) || (spp != 1)){
		fprintf(stderr, "Either undefined or unsupported number of samples per pixel\n");
		exit(42);
	}

	// Read in the possibly multiple strips
	stripSize = TIFFStripSize (image);
	stripMax = TIFFNumberOfStrips (image);
	imageOffset = 0;
	bufferSize = TIFFNumberOfStrips (image) * stripSize;
	if((buffer = (unsigned char *) malloc(bufferSize)) == NULL){
		fprintf(stderr, "Could not allocate enough memory for the uncompressed image\n");
		exit(42);
	}

	for (stripCount = 0; stripCount < stripMax; stripCount++){
		if((result = TIFFReadEncodedStrip (image, stripCount, buffer + imageOffset, stripSize)) == -1){
			fprintf(stderr, "Read error on input strip number %d\n", stripCount);
			exit(42);
		}
		imageOffset += result;
	}

	// Deal with photometric interpretations
	if(TIFFGetField(image, TIFFTAG_PHOTOMETRIC, &photo) == 0){
		fprintf(stderr, "Image has an undefined photometric interpretation\n");
		exit(42);
	}

	if(photo == PHOTOMETRIC_MINISWHITE){
		// Flip bits
		printf("Fixing the photometric interpretation\n");
		for(count = 0; count < bufferSize; count++)
			buffer[count] = ~buffer[count];
	}

	// Deal with fillorder
	if(TIFFGetField(image, TIFFTAG_FILLORDER, &fillorder) == 0){
		fprintf(stderr, "Image has an undefined fillorder, assuming MSB -> LSB (left to right)\n");
		fillorder = FILLORDER_MSB2LSB;
//		exit(42);
	}

	if(fillorder != FILLORDER_MSB2LSB){
		// We need to swap bits -- ABCDEFGH becomes HGFEDCBA
		printf("Fixing the fillorder\n");
		for(count = 0; count < bufferSize; count++){
			tempbyte = 0;
			if(buffer[count] & 128) tempbyte += 1;
			if(buffer[count] & 64) tempbyte += 2;
			if(buffer[count] & 32) tempbyte += 4;
			if(buffer[count] & 16) tempbyte += 8;
			if(buffer[count] & 8) tempbyte += 16;	
			if(buffer[count] & 4) tempbyte += 32;
			if(buffer[count] & 2) tempbyte += 64;
			if(buffer[count] & 1) tempbyte += 128;
			buffer[count] = tempbyte;
		}
	}

	// Extract image attributes width and height
	if(TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width) == 0){
		fprintf(stderr, "Image does not define its width\n");
		exit(42);
	} else {
		fprintf(stdout,"Image width = %d pixels\n",width);
	}

	if(TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height) == 0){
		fprintf(stderr, "Image does not define its width\n");
		exit(42);
	} else {
		fprintf(stdout,"Image height = %d pixels\n",height);
	}

	// Do whatever it is we do with the buffer -- we dump it in hex
	/*
	fprintf(stdout,"00000000: ");
	for(count = 0; count < bufferSize; count++){
		fprintf(stdout,"0x%02x ", (unsigned char) buffer[count]);
		if( ((count + 1) % (unsigned short) width ) == 0 ) {
			fprintf(stdout,"\n");
			fprintf(stdout,"%08d: ", (int) (count+1));
		}
	}
	*/

	TIFFClose(image);
	// >>>

	/* MODIFY THE IMAGE */
	// <<<
	
	width=712;
	fprintf(stdout, "Chnaged width of image to: %d\n", width);

	// Allocate new image buffer. We need both buffer and resulting image
	if((mate = (unsigned char *) calloc((width*height),sizeof(unsigned char))) == NULL){
		fprintf(stderr, "Could not allocate enough memory for the uncompressed image\n");
		exit(42);
	}

	if((transformedImg = (unsigned char *) calloc((width*height),sizeof(unsigned char))) == NULL){
		fprintf(stderr, "Could not allocate enough memory for the uncompressed image\n");
		exit(42);
	}

	// Copy the original image inside the resized mate
	for( row = 0; row < height; row++ ) {
		for( col = 100; col < 612; col++ ) {
			mate[(row*width)+col] = buffer[(row*512)+(col-100)];
		}
	}	

	// Inverse transformation matrix elements, claculated in matlab:
	float a[] = { 9.1894e-01, -3.3784e-02, 2.1250e+01, 0.0, 9.1881e-01, 8.1053e-02, 0.0, -1.2886e-04 };

	// Runt perspective transformation & inverse mapping convolution...
	for( row=0; row < height; row++ ) {
		for( col=0; col < width; col++ ) {
			idx = col-100;
			if((idx > -22) && (idx < 534 )) {
				x=(a[0]*idx+a[1]*row+a[2])/(a[6]*idx+a[7]*row+1);
				frc=x-floor(x);
				lookup=(uint32) row*width+(floor(x)+100);
				intensity0=mate[lookup];
				lookup=(uint32) row*width+(ceil(x)+100);
				intensity1=mate[lookup];

				intensity=(unsigned char) intensity0+(frc*(intensity1-intensity0));
				transformedImg[row*width+col]=intensity;
			}
		}
	}

	// >>>

	/* WRITE MODIFIED IMAGE */
	// <<<

	TIFF *out= TIFFOpen("new.tif", "w");

	// char *image=new char [width*height*sampleperpixel];

	/*
	 * Then format of the pixel information is store in the order RGBA, into the array, each channel occupies 1 byte (char).
	 * Now we need to set the tags in the new image file, and the essential ones are the following:
	 */

	TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);  // set the width of the image
	TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);    // set the height of the image
	TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, spp);   // set number of channels per pixel
	TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bps);    // set the size of the channels
	TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);    // set the origin of the image.
	//	Some other essential fields to set that you do not have to understand for now.
	TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
	TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);

	/*
	 * We will use most basic image data storing method provided by the library to write the data into the file, 
	 * this method uses strips, and we are storing a line (row) of pixel at a time.  
	 * This following code writes the data from the char array image into the file:
	 */

	tsize_t linebytes = spp * width;     // length in memory of one row of pixel in the image.

	unsigned char *wbuf = NULL;  
	/* buffer used to store the row of pixel information for writing to file
	 * Allocating memory to store the pixels of current row
	 */

	if (TIFFScanlineSize(out) == linebytes)
		wbuf =(unsigned char *)_TIFFmalloc(linebytes);
	else
		wbuf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(out));

	// We set the strip size of the file to be size of one row of pixels
	// TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(out, width*spp));
	TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, 64);

	//Now writing image to the file one strip at a time
	for (row = 0; row < height; row++) {
		// memcpy(wbuf, &buffer[row*linebytes], linebytes);    // check the index here, and figure out why not using h*linebytes
		memcpy(wbuf, &transformedImg[row*linebytes], linebytes);    // check the index here, and figure out why not using h*linebytes
		if (TIFFWriteScanline(out, wbuf, row, 0) < 0)
			break;
	}

	//Finally we close the output file, and destroy the buffer

	(void) TIFFClose(out);

	if (wbuf)
		_TIFFfree(wbuf);
	// >>>
}

signed short readImage( char * name, char ** pBuff ) {
/* Allocte image memory inside the function and return the 
 * content through pointer back to caling application
 */
	 *pBuff = NULL;

	if((*pBuff = (char *) malloc(12)) == NULL){
		printf("Error allocating memory\n");
		return -1;
	}

	(*pBuff)[0] = (char) 0x65;
	(*pBuff)[1] = (char) 0x66;
	(*pBuff)[2] = (char) 0x67;

	return 0;
}
